package edu.calpoly.csc.coursebook.util.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import net.didion.jwnl.JWNL;
import net.didion.jwnl.JWNLException;
import net.didion.jwnl.data.IndexWord;
import net.didion.jwnl.data.IndexWordSet;
import net.didion.jwnl.data.PointerType;
import net.didion.jwnl.data.PointerUtils;
import net.didion.jwnl.data.Synset;
import net.didion.jwnl.data.Word;
import net.didion.jwnl.data.list.PointerTargetTree;
import net.didion.jwnl.data.list.PointerTargetTreeNode;
import net.didion.jwnl.data.list.PointerTargetTreeNodeList;
import net.didion.jwnl.data.relationship.AsymmetricRelationship;
import net.didion.jwnl.data.relationship.Relationship;
import net.didion.jwnl.data.relationship.RelationshipFinder;
import net.didion.jwnl.data.relationship.RelationshipList;
import net.didion.jwnl.dictionary.Dictionary;
import edu.calpoly.csc.coursebook.Messages;
import edu.calpoly.csc.coursebook.util.KeywordExtractor;


public class WordNetKeywordExtractor implements KeywordExtractor {

	HashMap<String, Double> completeInterestKeywords;
	List<String> originalInterestKeywords;
	HashMap<String, String> interestToSynonymMap;
	static final int MAX_DEPTH = 1;
	static final String FILE_PROPERTIES = Messages.getString("wordnet.PROPSFILE");

	private static HashSet<String> INVALID_WORDS = new HashSet<String>();

	static {
		// add from a file
		try {
			BufferedReader rdr = new BufferedReader(new FileReader(
					new File(Messages.getString("wordnet.STOPLIST"))));
			String str = null;
			while ((str = rdr.readLine()) != null)
				INVALID_WORDS.add((str.toUpperCase()).trim());

//			System.out.println(INVALID_WORDS.size());
//
//			for (String str2: INVALID_WORDS)
//				System.out.println(str2);

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private static KeywordExtractor _instance;
	public static KeywordExtractor getInstance()
	{
		if (null == _instance)
			_instance = new WordNetKeywordExtractor();

		return _instance;
	}

	private WordNetKeywordExtractor() {
		completeInterestKeywords = new HashMap<String, Double>();
		interestToSynonymMap = new HashMap<String, String>();
		try {
			// initialize JWNL (this must be done before JWNL can be used)
			JWNL.initialize(new FileInputStream(FILE_PROPERTIES));
			//new Examples().go();
		} catch (Exception ex) {
			ex.printStackTrace();
			System.exit(-1);
		}
	}
	
	public List<String> getOriginalInterestKeywords() {
		return originalInterestKeywords;
	}
	public HashMap<String, Double> extractKeywords(String input)
	{
		this.completeInterestKeywords.clear();
		/* parse out invalid words */
		String[] interestWords = this.getKeywords(input);
		interestWords = checkValidWords(interestWords);

		/* parse out the non nouns, verbs, adjectives */
		List<String> interestKeywords = this.getNounsVerbsAdjectives(interestWords);
		
		for (String str : interestKeywords) {
			System.out.println(str);
		}
		
		originalInterestKeywords = interestKeywords;
		/*
                if(interestKeywords == null || interestKeywords.isEmpty()) {
                	interestKeywords = new ArrayList<String>(interestWords.length);
                	for (String word : interestWords)
                		interestKeywords.add(word);

                }
		 */
		this.addRelatedWordsToInterestKeywords(interestKeywords);


//		Collection<String> result = new ArrayList<String>(this.completeInterestKeywords.size());
//		for (String s: this.completeInterestKeywords)
//		{
//		result.add(s);
//		}
//		return result;
		return completeInterestKeywords;
	}

	public String[] checkValidWords(String[] input) {
		StringBuffer strBuff = new StringBuffer();
		ArrayList<String> output = new ArrayList<String>();

		for (String str: input) {
			if (!INVALID_WORDS.contains(str.toUpperCase())) {
				//strBuff.append(str + ", ");
				output.add(str);
			}
		}
		String[] outputString = new String[output.size()];
		output.toArray(outputString);
		return outputString;
	}

	public void addRelatedWordsToInterestKeywords(List<String> interestKeywords) {
		// Get all of the hypernyms (parents) of all senses of all words in the parameter
		try {
			/* for each word in the interest keywords list */
			for(String word : interestKeywords) {
				addToInterestKeywords(word, word, 1);
				IndexWordSet wordSet;
				wordSet = Dictionary.getInstance().lookupAllIndexWords(word);
				/* for each POS for the word */
				for (IndexWord indexWord : wordSet.getIndexWordArray()) {
					/* get the hypernym tree */
					try { 
						PointerTargetTree hypernymstree = PointerUtils.getInstance().getHypernymTree(indexWord.getSense(1)); 
						PointerTargetTreeNode rootNode = hypernymstree.getRootNode(); 
						Word [] words = rootNode.getSynset().getWords(); 

						/* add the synonyms of the root to the interest keywords list */
						for(int i=0; i<words.length; i++) { 
							addToInterestKeywords(word, words[i].getLemma(), 1);
						} 

						/* get the children and add them and their synonyms to the interest keywords list */
						PointerTargetTreeNodeList childTree = rootNode.getChildTreeList(); 
						for(Object child : childTree) {
							Synset syn = ((PointerTargetTreeNode)child).getSynset(); 
							Word [] wordarray = syn.getWords(); 
							for(int i=0; i<wordarray.length; i++) { 
								addToInterestKeywords(word, wordarray[i].getLemma(), 1);
							}
							traverseHypernymTree(word, (PointerTargetTreeNode)child, 1);
						}
					} catch (NullPointerException ex) {
						/* no hypernym tree */
					}
				}
			}//end for
		} catch (JWNLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} 
	}

	public boolean asymmetricRelationshipsExists(IndexWord start, IndexWord end) throws JWNLException {
		// Try to find a relationship between the first sense of <var>start</var> and the first sense of <var>end</var>
		RelationshipList list;
		try {
			list = RelationshipFinder.getInstance().findRelationships(start.getSense(1), end.getSense(1), PointerType.HYPERNYM);
			System.out.println("Hypernym relationship between \"" + start.getLemma() + "\" and \"" + end.getLemma() + "\":");
			for (Iterator itr = list.iterator(); itr.hasNext();) {
				((Relationship) itr.next()).getNodeList().print();
			}
		} catch(NullPointerException ex) {
			System.err.println("no hypernym relationship between " + start.getLemma() + "\" and \"" + end.getLemma() + "\":");
			return false;
		}


		if(!(list.isEmpty())) {
			System.out.println("Common Parent Index: " + ((AsymmetricRelationship) list.get(0)).getCommonParentIndex());
			System.out.println("Depth: " + ((Relationship) list.get(0)).getDepth());
			return true;
		} else {
			System.err.println("no hypernym relationship between \"" + start.getLemma() + "\" and \"" + end.getLemma() + "\":");
			return false;
		}
	}

	/* Takes in a String of the user's interests and returns a String[] of all the words */
	public String[] getKeywords(String interests) {
		String[] interestKeywords = interests.split("\\s|[^\\w]|\\d");

		return interestKeywords;
	}

	public void traverseHypernymTree(String root, PointerTargetTreeNode item, int count) {

		if(item.hasChildTreeList()) {
			for(Object child : item.getChildTreeList()) {
				Synset syn = ((PointerTargetTreeNode)child).getSynset(); 
				Word [] wordarray = syn.getWords(); 
				for(int i=0; i<wordarray.length; i++){ 
					addToInterestKeywords(root, wordarray[i].getLemma(), count);
				}
				count = count+1;
				if(count <= MAX_DEPTH) {
					traverseHypernymTree(root, (PointerTargetTreeNode)child, count);
				}
			}
		}

	}

	/* creates an intial (not complete) list of interest keywords */
	private List<String> getNounsVerbsAdjectives(String[] words) {
		List<String> interestKeywords = new ArrayList<String>();
		for (String word : words) {
			IndexWordSet wordSet;
			try {
				wordSet = Dictionary.getInstance().lookupAllIndexWords(word);


				for (IndexWord oneWord : wordSet.getIndexWordArray()) {
					if((oneWord.getPOS().getLabel()).equalsIgnoreCase("NOUN") ||
							(oneWord.getPOS().getLabel()).equalsIgnoreCase("VERB") ||
							(oneWord.getPOS().getLabel()).equalsIgnoreCase("ADJECTIVE")) {
						if(!interestKeywords.contains(word)) 
							interestKeywords.add(word);
					}

				}
			} catch (JWNLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return interestKeywords;
	}

	/* adds words to the complete list of interest keywords */
	private void addToInterestKeywords(String word, double level) {
		if(!completeInterestKeywords.containsKey(word))
			completeInterestKeywords.put(word.toLowerCase(), level);
	}

	/* adds words to the complete list of interest keywords */
	private void addToInterestKeywords(String original, String word, double level) {
		if(!completeInterestKeywords.containsKey(word)) {
//			System.out.println("key: " + word + " value: " + original);
			interestToSynonymMap.put(word, original);
			completeInterestKeywords.put(word.toLowerCase(), level);
		}
	}

	public void printInterestKeywords() {
		System.out.println("# of keywords = " + completeInterestKeywords.size());
//		for (String word : completeInterestKeywords) {
//		System.out.println(word);
//		}
	}

	public HashMap<String, Double> getInterestKeywords() {
		return completeInterestKeywords;
	}
	
	public HashMap<String, String> getInterestToSynonymMap() {
		return interestToSynonymMap;
	}

	public static void main(String[] args)
	{
		KeywordExtractor wordNet = WordNetKeywordExtractor.getInstance();
		String input = "sports dance";
		wordNet.extractKeywords(input);
//		Collection<String> words = wordNet.extractKeywords(input);
//		words = wordNet.extractKeywords("writing");

//		System.out.println("Total words: " + words.size());
//		int i = 0;
//		for (String s: words)
//		{
//		System.out.println(i++ + ": " + s);
//		}
	}
	/*
	public static void main(String[] args) {
		try {
			// initialize JWNL (this must be done before JWNL can be used)
			JWNL.initialize(new FileInputStream(FILE_PROPERTIES));
			//new Examples().go();
		} catch (Exception ex) {
			ex.printStackTrace();
			System.exit(-1);
		}

		WordNetKeywordExtractor wordNet = new WordNetKeywordExtractor();

		String interests = "(Anthropological) perspective of traditional and contemporary food customs and culture. Major emphasis on U.S. cultures including Native American, Hispanic American, African American, and Asian American. Past and future developments in organic foods, junk foods and industrial foods. 4 lectures.";
		/* parse out the non nouns, verbs, adjectives * /
		String[] interestWords = wordNet.getKeywords(interests);
		List<String> interestKeywords = wordNet.getNounsVerbsAdjectives(interestWords);

		wordNet.addRelatedWordsToInterestKeywords(interestKeywords);

		wordNet.printInterestKeywords();

	}
	 */
}
